﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using NetCore.ORM;

namespace NetCore
{
    /// <summary>
    /// 将检索规则 翻译成 where sql 语句,并生成相应的参数列表
    /// 如果遇到{CurrentUserID}这种，翻译成对应的参数
    /// </summary>
    public class TranslatorFilter
    {
        //几个前缀/后缀
        protected static char leftToken = '[';
        protected static char paramPrefixToken = ':';
        protected static char rightToken = ']';
        protected static char groupLeftToken = '(';
        protected static char groupRightToken = ')';
        protected static char likeToken = '%';
        
        #region ICriterion
        public static ICriterion CoreTranslate(ICriterion expression, GroupFilter group, string groupOp, 
            StringBuilder bulider, List<FilterParam> filterParams)
        {

            if (group == null) return null;
            var appended = false;
            Dictionary<string, string> dicPararm = new Dictionary<string, string>();
            if (group.rules != null)
            {
                for (int i = 0; i < group.rules.Count; i++)
                {
                    ICriterion where = CoreTranslateRule(group, group.rules[i], group.op, dicPararm);
                    if (where == null)
                    {
                        continue;
                    }
                    if (appended)
                    {
                        bulider.Append(GetOperatorQueryText(group.op));
                        switch (group.op.ToLower())
                        {
                            case "and":
                                expression = Expression.And(expression, where);
                                break;
                            case "or":
                                expression = Expression.Or(expression, where);
                                break;
                        }

                    }
                    else
                    {
                        if (expression == null)
                        {
                            expression = where;
                        }
                        else
                        {
                            switch (group.op.ToLower())
                            {
                                case "and":
                                    expression = Expression.And(expression, where);
                                    break;
                                case "or":
                                    expression = Expression.Or(expression, where);
                                    break;
                            }
                        }
                    }

                    bulider.Append(TranslateRule(group.rules[i], filterParams));
                    appended = true;
                }
            }
            if (group.groups != null)
            {
                foreach (var subgroup in group.groups)
                {
                    if (appended)
                    {
                        bulider.Append(GetOperatorQueryText(group.op));
                    }
                    expression = CoreTranslate(expression, subgroup, subgroup.op, bulider, filterParams);
                    appended = true;
                }
            }
            return expression;
        }


        /// <summary>
        /// where构建
        /// </summary>
        /// <param name="rule"></param>
        /// <param name="groupOp"></param>
        /// <returns></returns>
        public static ICriterion CoreTranslateRule(GroupFilter group, RuleFilter rule, string groupOp, Dictionary<string, string> dicPararm)
        {
            ICriterion where = null;
            //if (rule.value != null)
            //{
            //    if (rule.type.EqualsTo("int", true) || rule.type.EqualsTo("digits", true))
            //        rule.value = Convert.ToInt32(rule.value);
            //    else if (rule.type.EqualsTo("float", true) || rule.type.EqualsTo("number", true))
            //        rule.value = Convert.ToDecimal(rule.value);
            //}
            if (dicPararm.ContainsKey(rule.field))
            {
                return null;
            }
            //return bulider.ToString();
            switch (rule.op.ToLower())
            {

                case "equal":
                    where = Expression.Eq(rule.field, rule.value);
                    break;
                case "notequal":
                    where = Expression.Not(Expression.Eq(rule.field, rule.value));
                    break;
                case "greater"://>
                    where = Expression.Gt(rule.field, rule.value);
                    break;
                case "greaterorequal"://>=
                    where = Expression.Ge(rule.field, rule.value);
                    break;
                case "isnull": //isnull
                    where = Expression.IsNull(rule.field);
                    break;
                case "isnotnull"://is not null
                    where = Expression.IsNotNull(rule.field);
                    break;
                case "less": //<
                    where = Expression.Lt(rule.field, rule.value);
                    break;
                case "lessorequal"://<=
                    where = Expression.Le(rule.field, rule.value);
                    break;
                case "like": // like
                    where = Expression.Like(rule.field, rule.value );
                    break;
                case "startwith"://startwith
                    dicPararm.Add(rule.field, "startwith");
                    var endRule = group.rules.Where(x => x.field == rule.field && x.op.ToLower().Equals("endwith")).FirstOrDefault();
                    where = Expression.Between(rule.field, rule.value, (endRule != null ? endRule.value : null));
                    break;
                case "endwith"://endwith
                    dicPararm.Add(rule.field, "endwith");
                    var startrule = group.rules.Where(x => x.field == rule.field && x.op.ToLower().Equals("endwith")).FirstOrDefault();
                    where = Expression.Between(rule.field, (startrule != null ? startrule.value : null), rule.value);
                    break;
                case "in":
                    where = Expression.In(rule.field, rule.value.ToString().Split(new char[] { ',', ';' }));
                    break;
                case "notin":
                    where = Expression.Not(Expression.In(rule.field, rule.value.ToString().Split(new char[] { ',', ';' })));
                    break;
                default: //
                    where = Expression.Eq(rule.field, rule.value);
                    break;
            }
            return where;
        }
        #endregion
        public static string TranslateRule(RuleFilter rule, List<FilterParam> filterParams)
        {
            StringBuilder bulider = new StringBuilder();
            if (rule == null) return " 1=1 ";

            bulider.Append(leftToken + rule.field + rightToken);
            //操作符
            bulider.Append(GetOperatorQueryText(rule.op));

            var op = rule.op.ToLower();
            if (op == "like" || op == "endwith")
            {
                var value = rule.value.ToString();
                if (!value.StartsWith(likeToken.ToString()))
                {
                    rule.value = likeToken + value;
                }
            }
            if (op == "like" || op == "startwith")
            {
                var value = rule.value.ToString();
                if (!value.EndsWith(likeToken.ToString()))
                {
                    rule.value = value + likeToken;
                }
            }
            if (op == "in" || op == "notin")
            {
                var values = rule.value.ToString().Split(',');
                var appended = false;
                bulider.Append("(");
                foreach (var value in values)
                {
                    if (appended) bulider.Append(",");

                    bulider.Append(paramPrefixToken + CreateFilterParam(value, rule.type, filterParams));

                    appended = true;
                }
                bulider.Append(")");
            }
            //is null 和 is not null 不需要值
            else if (op != "isnull" && op != "isnotnull")
            {
                bulider.Append(paramPrefixToken + CreateFilterParam(rule.value, rule.type, filterParams));

            }
            return bulider.ToString();
        }

        private static string CreateFilterParam(object value, string type, List<FilterParam> filterParams)
        {
            string paramName = "p" + (filterParams.Count);
            object val = value;
            if (type.EqualsTo("int", true) || type.EqualsTo("digits", true))
                val = val.ToInt();
            else if (type.EqualsTo("float", true) || type.EqualsTo("number", true))
                val = val.ToDecimal();
            FilterParam param = new FilterParam(paramName, val);
            filterParams.Add(param);
            return paramName;
        }
      

        #region 公共工具方法
        /// <summary>
        /// 获取操作符的SQL Text
        /// </summary>
        /// <param name="op"></param>
        /// <returns></returns> 
        public static string GetOperatorQueryText(string op)
        {
            switch (op.ToLower())
            {
                case "add":
                    return " + ";
                case "bitwiseand":
                    return " & ";
                case "bitwisenot":
                    return " ~ ";
                case "bitwiseor":
                    return " | ";
                case "bitwisexor":
                    return " ^ ";
                case "divide":
                    return " / ";
                case "equal":
                    return " = ";
                case "greater":
                    return " > ";
                case "greaterorequal":
                    return " >= ";
                case "isnull":
                    return " is null ";
                case "isnotnull":
                    return " is not null ";
                case "less":
                    return " < ";
                case "lessorequal":
                    return " <= ";
                case "like":
                    return " like ";
                case "startwith":
                    return " like ";
                case "endwith":
                    return " like ";
                case "modulo":
                    return " % ";
                case "multiply":
                    return " * ";
                case "notequal":
                    return " <> ";
                case "subtract":
                    return " - ";
                case "and":
                    return " and ";
                case "or":
                    return " or ";
                case "in":
                    return " in ";
                case "notin":
                    return " not in ";
                default:
                    return " = ";
            }
        }
        #endregion

    }
}
